package net.jueb.protobufs.codec;

import static io.netty.buffer.Unpooled.wrappedBuffer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

import java.io.ByteArrayOutputStream;
import java.util.HashMap;

import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.MessageLite;
import com.google.protobuf.MessageLiteOrBuilder;

/**
 * 为编码后的数据追加长度的同时，增加一个类型标记
 * 带半包处理
 * ProtobufVarint32LengthFieldPrepender+ProtobufEncoder的融合改造
 * @author Administrator
 *
 */
@Sharable
public class ProtobufsEncoder extends MessageToByteEncoder<MessageLiteOrBuilder> {
	private  HashMap<Byte,MessageLite> encoders;
	public ProtobufsEncoder(HashMap<Byte,MessageLite> encoders) {
		this.encoders=encoders;
	}
    @Override
    protected void encode(ChannelHandlerContext ctx, MessageLiteOrBuilder msg,ByteBuf out) throws Exception 
    {
    	MessageLite protoModel=null;
    	if (msg instanceof MessageLite) 
        {
        	protoModel=(MessageLite) msg;
        }
        if (msg instanceof MessageLite.Builder) 
        {
        	protoModel=((MessageLite.Builder) msg).build();
        }
        if(protoModel!=null)
        {
        	for(Byte type:encoders.keySet())
        	{
        		if(protoModel.getClass().equals(encoders.get(type).getDefaultInstanceForType().getClass()))
        		{//如果是相同的类
        			ByteArrayOutputStream bos=new ByteArrayOutputStream();
        			bos.write(type);//写入模型类型头
        			bos.write(protoModel.toByteArray());//写入模型数据
        			bos.flush();
        			ByteBuf data=wrappedBuffer(bos.toByteArray());//得到类型+数据
        			sendData(data, out);
        			return;
        		}
        	}
        }
   
    }
    
    private void sendData(ByteBuf data,ByteBuf out)throws Exception
    {
    	//追加长度头
		int bodyLen = data.readableBytes();
        int headerLen = CodedOutputStream.computeRawVarint32Size(bodyLen);
        out.ensureWritable(headerLen + bodyLen);

        CodedOutputStream headerOut =
                CodedOutputStream.newInstance(new ByteBufOutputStream(out));
        headerOut.writeRawVarint32(bodyLen);
        headerOut.flush();
        out.writeBytes(data, data.readerIndex(), bodyLen);
    }

}
